package com.csea.app.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.csea.app.model.vo.idiom.*;
import com.csea.app.service.PublicIdiomService;
import com.csea.app.service.UserIdiomService;
import com.csea.app.service.UserRepositoryService;
import com.csea.common.domain.dos.PublicIdiomDo;
import com.csea.common.domain.dos.UserIdiomDo;
import com.csea.common.domain.dos.UserRepositoryDo;
import com.csea.common.domain.mapper.PublicIdiomMapper;
import com.csea.common.domain.mapper.UserIdiomMapper;
import com.csea.common.enums.ResponseCodeEnum;
import com.csea.common.utils.Response;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class UserIdiomServiceImpl extends ServiceImpl<UserIdiomMapper, UserIdiomDo> implements UserIdiomService {

    private final UserRepositoryService repositoryService;
    private final PublicIdiomMapper publicIdiomMapper;
    private final UserIdiomMapper userIdiomMapper;
    private final PublicIdiomService publicIdiomService;

    @Override
    @Transactional
    public Response<Boolean> saveUserIdiom(AddUserIdiomReqVO reqVO) {
        // 校验仓库ID有效性
        if (reqVO.getRepositoryId() != null) {
            Response<Boolean> checkResult = checkRepositoryExists(reqVO.getRepositoryId());
            if (!checkResult.isSuccess()) {
                return checkResult;
            }
        }

        UserIdiomDo entity = convertToDo(reqVO);
        entity.setUserId(StpUtil.getLoginIdAsInt());
        return Response.success(this.save(entity));
    }

    @Override
    public Response<UserIdiomRspVO> getUserIdiomById(Integer idiomId) {
        UserIdiomDo idiomDo = this.getById(idiomId);
        if (idiomDo == null) {
            return Response.fail(ResponseCodeEnum.USER_IDIOM_NOT_FOUND_ERROR);
        }
        return Response.success(convertToRspVO(idiomDo));
    }

    @Override
    @Transactional
    public Response<Boolean> updateUserIdiomBaseInfo(UpdateUserIdiomBaseInfoReqVO reqVO) {
        // 校验数据存在性
        UserIdiomDo existing = this.getById(reqVO.getUserIdiomId());
        if (existing == null) {
            return Response.fail(ResponseCodeEnum.USER_IDIOM_NOT_FOUND_ERROR);
        }

        Response<Boolean> booleanResponse = checkRepositoryExists(reqVO.getRepositoryId());
        if (!booleanResponse.isSuccess()) {
            return booleanResponse;
        }

        UserIdiomDo userIdiomDo = new UserIdiomDo();
        BeanUtils.copyProperties(reqVO, userIdiomDo);

        // 保留原始用户ID防止篡改
        userIdiomDo.setUserId(existing.getUserId());
        return Response.success(this.updateById(userIdiomDo));
    }

    @Override
    @Transactional
    public Response<Boolean> deleteUserIdiom(Integer idiomId) {
        // 校验归属权
        UserIdiomDo existing = this.getById(idiomId);
        if (existing == null) {
            return Response.fail(ResponseCodeEnum.USER_IDIOM_NOT_FOUND_ERROR);
        }

        if (!existing.getUserId().equals(StpUtil.getLoginIdAsInt())) {
            return Response.fail(ResponseCodeEnum.PERMISSION_DENIED_ERROR);
        }

        return Response.success(this.removeById(idiomId));
    }

    @Override
    public Response<List<UserIdiomRspVO>> listByRepository(Integer repositoryId) {
        // 校验仓库ID有效性
        Response<Boolean> checkResult = checkRepositoryExists(repositoryId);
        if (!checkResult.isSuccess()) {
            return Response.success();
        }

        LambdaQueryWrapper<UserIdiomDo> query = Wrappers.lambdaQuery();
        query.eq(UserIdiomDo::getRepositoryId, repositoryId)
                .orderByDesc(UserIdiomDo::getUpdateTime);

        List<UserIdiomDo> idioms = this.list(query);
        if (CollectionUtils.isEmpty(idioms)) {
            return Response.success(Collections.emptyList());
        }

        return Response.success(idioms.stream()
                .map(this::convertToRspVO)
                .collect(Collectors.toList()));
    }

    /**
     *
     * @param reqVO 更新成语操作信息
     */
    @Override
    @Transactional
    public Response<Boolean> updateOperateInfo(UpdateUserIdiomOperateInfoReqVO reqVO) {
        // 1. 校验数据归属权
        UserIdiomDo existing = this.getById(reqVO.getUserIdiomId());
        if (existing == null) {
            return Response.fail(ResponseCodeEnum.USER_IDIOM_NOT_FOUND_ERROR);
        }

        if (!existing.getUserId().equals(StpUtil.getLoginIdAsInt())) {
            return Response.fail(ResponseCodeEnum.PERMISSION_DENIED_ERROR);
        }

        // 2. 创建更新对象
        UserIdiomDo updateDo = new UserIdiomDo();
        BeanUtils.copyProperties(reqVO, updateDo);

        // 3. 执行更新
        return Response.success(this.updateById(updateDo));
    }

    /**
     * 根据关键字搜索成语
     *
     * @param keyword 关键字
     * @return 成语列表
     */
    @Override
    public Response<List<UserIdiomRspVO>> searchIdiomsByKeyword(String keyword) {
        Integer userId = StpUtil.getLoginIdAsInt();

        // 优先查询用户词库（包含近义词）
        List<UserIdiomDo> userResults = userIdiomMapper.selectByUserIdAndKeyword(userId, keyword);
        if (!CollectionUtils.isEmpty(userResults)) {
            return Response.success(convertToRspVOList(userResults));
        }

        // 查询公共词库（包含近义词）
        List<PublicIdiomDo> publicResults = publicIdiomMapper.selectByKeywordLike(keyword);
        return Response.success(convertPublicToRspVO(publicResults));
    }

    /**
     * 根据关键词搜索成语coze服务
     * @param keyword 关键词
     * @return 成语列表
     */
    @Override
    public Response<List<UserIdiomRspVO>> searchIdiomByCoze(String keyword) {
        boolean b = publicIdiomService.searchAndSavePublicIdiomByCoze(keyword);
        if (b) {
         return searchIdiomsByKeyword(keyword);
        }
        return Response.fail(ResponseCodeEnum.KEYWORD_NOT_VALID_ERROR);
    }


    /**
     * 获取用户卡片统计信息
     * @return 用户卡片统计信息
     */
    @Override
    public Response<UserCardStatsRspVO> getUserCardStats() {
        Integer userId = StpUtil.getLoginIdAsInt();
        return Response.success(UserCardStatsRspVO.builder()
                .newCardCount(Integer.valueOf(userIdiomMapper.countNewCards(userId).toString()))
                .reviewCardCount(Integer.valueOf(userIdiomMapper.countReviewCards(userId).toString()))
                .build());
    }

    // 新增转换方法
    private List<UserIdiomRspVO> convertToRspVOList(List<UserIdiomDo> list) {
        return list.stream()
                .map(this::convertToRspVO)
                .collect(Collectors.toList());
    }

    private List<UserIdiomRspVO> convertPublicToRspVO(List<PublicIdiomDo> list) {
        return list.stream().map(p -> {
            UserIdiomRspVO vo = new UserIdiomRspVO();
            BeanUtils.copyProperties(p, vo);
            vo.setPublicIdiomId(p.getIdiomId()); // 标记为公共词库
            vo.setRepositoryId(0);
            return vo;
        }).collect(Collectors.toList());
    }

    private UserIdiomRspVO convertToRspVO(PublicIdiomDo idiomDo) {
        UserIdiomRspVO vo = new UserIdiomRspVO();
        BeanUtils.copyProperties(idiomDo, vo);
        vo.setPublicIdiomId(idiomDo.getIdiomId());
        vo.setRepositoryId(0);
        return vo;
    };

    private Response<Boolean> checkRepositoryExists(Integer repositoryId) {
        try {
            // 获取仓库详情并校验归属权
            UserRepositoryDo userRepositoryDo = repositoryService.getById(repositoryId);
            if (userRepositoryDo == null) {
                return Response.fail(ResponseCodeEnum.REPOSITORY_NOT_FOUND_ERROR);
            }

            // 验证当前用户是否为仓库所有者
            Integer currentUserId = StpUtil.getLoginIdAsInt();
            if (!currentUserId.equals(userRepositoryDo.getUserId())) {
                return Response.fail(ResponseCodeEnum.PERMISSION_DENIED_ERROR);
            }

            return Response.success(true);
        } catch (Exception e) {
            return Response.fail(ResponseCodeEnum.REPOSITORY_NOT_FOUND_ERROR);
        }
    }

    private UserIdiomDo convertToDo(AddUserIdiomReqVO reqVO) {
        UserIdiomDo doObj = new UserIdiomDo();
        BeanUtils.copyProperties(reqVO, doObj);
        return doObj;
    }

    private UserIdiomRspVO convertToRspVO(UserIdiomDo idiomDo) {
        UserIdiomRspVO vo = new UserIdiomRspVO();
        BeanUtils.copyProperties(idiomDo, vo);
        // 可添加额外字段转换
        return vo;
    }


    /**
     * 处理复习结果
     * @param idiom 词语对象
     * @param reviewResult 复习结果
     */
    private void processReview(UserIdiomDo idiom, String reviewResult) {
        // 处理久未复习的情况（超过间隔2倍）
        if (isOverdueReview(idiom)) {
            handleOverdueReview(idiom, reviewResult);
            return;
        }

        // 更新记忆状态计数器
        updateReviewCounters(idiom, reviewResult);

        // 计算调整系数
        double factor = calculateAdjustmentFactor(idiom);

        // 初始间隔逻辑
        int currentInterval = idiom.getReviewInterval() != null ? idiom.getReviewInterval() : 0;
        if (currentInterval == 0) {
            currentInterval = getInitialInterval(idiom.getRememberCount());
        }

        // 应用调整系数
        int newInterval = (int) Math.round(currentInterval * factor);

        // 处理重修逻辑
        if (shouldResetProgress(idiom)) {
            resetLearningProgress(idiom);
            newInterval = 1;
        }

        // 更新复习时间
        updateReviewSchedule(idiom, newInterval);
    }

    // 辅助方法
    private boolean isOverdueReview(UserIdiomDo idiom) {
        if (idiom.getNextReviewTime() == null) return false;

        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        LocalDateTime nextReview = LocalDateTime.parse(idiom.getNextReviewTime(), formatter);
        long daysPassed = ChronoUnit.DAYS.between(nextReview, LocalDateTime.now());
        return daysPassed > idiom.getReviewInterval() * 2;
    }

    private void handleOverdueReview(UserIdiomDo idiom, String result) {
        if ("FORGET".equalsIgnoreCase(result)) {
            idiom.setForgetCount(idiom.getForgetCount() + 1);
            idiom.setReviewInterval(1);
        } else {
            idiom.setReviewInterval((int)(idiom.getReviewInterval() * 0.7));
        }
        updateReviewSchedule(idiom, idiom.getReviewInterval());
    }

    private void updateReviewCounters(UserIdiomDo idiom, String result) {
        switch (result.toUpperCase()) {
            case "REMEMBER":
                idiom.setRememberCount(idiom.getRememberCount() + 1);
                break;
            case "FUZZY":
                idiom.setFuzzyCount(idiom.getFuzzyCount() + 1);
                break;
            case "FORGET":
                idiom.setForgetCount(idiom.getForgetCount() + 1);
                break;
        }
    }

    private double calculateAdjustmentFactor(UserIdiomDo idiom) {
        if (idiom.getForgetCount() > 0) {
            return idiom.getForgetCount() == 1 ? 0.5 : 0.3;
        } else if (idiom.getFuzzyCount() > 0) {
            return idiom.getFuzzyCount() <= 2 ? 0.8 : 0.6;
        } else {
            return idiom.getRememberCount() >= 3 ? 1.8 : 1.5;
        }
    }

    private int getInitialInterval(int rememberCount) {
        int[] intervals = {1, 2, 4, 7};
        return rememberCount < intervals.length ? intervals[rememberCount] : intervals[intervals.length - 1];
    }

    private boolean shouldResetProgress(UserIdiomDo idiom) {
        return idiom.getForgetCount() >= 3 || idiom.getFuzzyCount() >= 5;
    }

    private void resetLearningProgress(UserIdiomDo idiom) {
        idiom.setRememberCount(0);
        idiom.setFuzzyCount(0);
        idiom.setForgetCount(0);
    }

    private void updateReviewSchedule(UserIdiomDo idiom, int intervalDays) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

        idiom.setLastReviewTime(now.format(formatter));
        idiom.setNextReviewTime(now.plusDays(intervalDays).format(formatter));
        idiom.setReviewInterval(intervalDays);

        // 更新卡片状态
        if (idiom.getRememberCount() >= 5) {
            idiom.setCardStatus(2); // MASTERED
        } else if (shouldResetProgress(idiom)) {
            idiom.setCardStatus(0); // NEW
        } else {
            idiom.setCardStatus(1); // REVIEW
        }
    }
}